#include "helper.h"
#include "monitor.h"
#include "reg.h"
#include "memory.h"
extern uint32_t instr;
extern char assembly[80];

/* invalid opcode */
make_helper(inv) {

	uint32_t temp;
	temp = instr_fetch(pc, 4);

	uint8_t *p = (void *)&temp;
	printf("invalid opcode(pc = 0x%08x): %02x %02x %02x %02x ...\n\n", 
			pc, p[3], p[2], p[1], p[0]);

	printf("There are two cases which will trigger this unexpected exception:\n\
1. The instruction at pc = 0x%08x is not implemented.\n\
2. Something is implemented incorrectly.\n", pc);
	printf("Find this pc value(0x%08x) in the disassembling result to distinguish which case it is.\n\n", pc);

	assert(0);
}

// privileged instr
make_helper(eret){
	cpu.pc=cpu.cp0.epc;
	cpu.cp0.status.EXL=0;
	sprintf(assembly, "eret");
}

static void decode_mfc0(uint32_t instr){
	op_src1->type=OP_TYPE_REG;
	op_src1->reg=(instr&0x0000F800)>>11;
	
	op_src2->type=OP_TYPE_IMM;
	op_src2->imm=(instr&0x00000007);

	op_dest->type=OP_TYPE_REG;
	op_dest->reg=(instr&0x001F0000)>>16;
}

make_helper(mfc0){
	decode_mfc0(instr);
	if(op_src1->reg==BadVAddr_Code&&op_src2->imm==0){
		reg_w(op_dest->reg)=cpu.cp0.BadVAddr;
		sprintf(assembly, "mfc0   %s, BadVAddr,  0x%04x", REG_NAME(op_dest->reg), op_src2->imm);
	}else if(op_src1->reg==Status_Code&&op_src2->imm==0){
		reg_w(op_dest->reg)=cpu.cp0.status.val;
		sprintf(assembly, "mfc0   %s, Status,  0x%04x", REG_NAME(op_dest->reg), op_src2->imm);
	}else if (op_src1->reg==Cause_Code&&op_src2->imm==0){
		reg_w(op_dest->reg)=cpu.cp0.cause.val;
		sprintf(assembly, "mfc0   %s, Cause,  0x%04x", REG_NAME(op_dest->reg), op_src2->imm);
	}else if (op_src1->reg==EPC_Code&&op_src2->imm==0){
		reg_w(op_dest->reg)=cpu.cp0.epc;
		sprintf(assembly, "mfc0   %s, EPC,  0x%04x", REG_NAME(op_dest->reg), op_src2->imm);
	}else {
		panic("[MFC0] Invalid cp0 register.\n");
	}
}

static void decode_mtc0(uint32_t instr){
	op_dest->type=OP_TYPE_REG;
	op_dest->reg=(instr&0x0000F800)>>11;//rd
	
	op_src2->type=OP_TYPE_IMM;
	op_src2->imm=(instr&0x00000007);//sel

	op_src1->type=OP_TYPE_REG;
	op_src1->reg=(instr&0x001F0000)>>16;//rt
}

make_helper(mtc0){
	decode_mtc0(instr);
	if(op_dest->reg==BadVAddr_Code&&op_src2->imm==0){
		cpu.cp0.BadVAddr=reg_w(op_src1->reg);
		sprintf(assembly, "mtc0   %s, BadVAddr,  0x%04x", REG_NAME(op_src1->reg), op_src2->imm);
	}else if (op_dest->reg==Status_Code&&op_src2->imm==0){
		cpu.cp0.status.val=reg_w(op_src1->reg);
		sprintf(assembly, "mtc0   %s, Status,  0x%04x", REG_NAME(op_src1->reg), op_src2->imm);
	}else if (op_dest->reg==Cause_Code&&op_src2->imm==0){
		cpu.cp0.cause.val=reg_w(op_src1->reg);
		sprintf(assembly, "mtc0   %s, Cause,  0x%04x", REG_NAME(op_src1->reg), op_src2->imm);
	}else if (op_dest->reg==EPC_Code&& op_src2->imm==0){
		cpu.cp0.epc=reg_w(op_src1->reg);
		sprintf(assembly, "mtc0   %s, EPC,  0x%04x", REG_NAME(op_src1->reg), op_src2->imm);
	}else {
		panic("[MTC0] Invalid cp0 register.\n");
	}
}

/* stop temu */
make_helper(temu_trap) {

	printf("\33[1;32mtemu: HIT GOOD TRAP\33[0m at $pc = 0x%08x\n\n", cpu.pc);

	temu_state = END;

}

make_helper(bad_temu_trap){
	printf("\33[1;31mtemu: HIT BAD TRAP\33[0m at $pc = 0x%08x\n\n", cpu.pc);
	temu_state = END;
}
